"""
路由策略抽象层

定义路由策略的接口和数据结构，支持多种路由算法。

核心概念：
- RoutingStrategy: 路由策略抽象基类
- RoutingContext: 路由上下文，包含请求信息
- RoutingResult: 路由结果，包含选中的适配器

作者: RedFire Team
创建日期: 2025-10-06
版本: v1.0
"""

from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import List, Optional, Dict, Any, Set
from enum import Enum
import logging


class RoutingPriority(Enum):
    """路由优先级枚举"""
    HIGHEST = 1
    HIGH = 2
    MEDIUM = 3
    LOW = 4
    LOWEST = 5


@dataclass
class RoutingContext:
    """
    路由上下文
    
    包含路由决策所需的所有信息。
    
    Attributes:
        request_type: 请求类型 (如: "get_quote", "get_kline", "get_transaction")
        symbols: 证券代码列表
        parameters: 请求参数
        required_capabilities: 必需的能力列表
        preferred_providers: 优先使用的数据源列表
        excluded_providers: 排除的数据源列表
        metadata: 额外的元数据
    """
    request_type: str
    symbols: List[str] = field(default_factory=list)
    parameters: Dict[str, Any] = field(default_factory=dict)
    required_capabilities: Set[str] = field(default_factory=set)
    preferred_providers: List[str] = field(default_factory=list)
    excluded_providers: List[str] = field(default_factory=list)
    metadata: Dict[str, Any] = field(default_factory=dict)
    
    def add_required_capability(self, capability: str):
        """添加必需的能力"""
        self.required_capabilities.add(capability)
    
    def has_required_capability(self, capability: str) -> bool:
        """检查是否有必需的能力"""
        return capability in self.required_capabilities
    
    def is_provider_excluded(self, provider_name: str) -> bool:
        """检查数据源是否被排除"""
        return provider_name in self.excluded_providers
    
    def is_provider_preferred(self, provider_name: str) -> bool:
        """检查数据源是否被优先"""
        return provider_name in self.preferred_providers


@dataclass
class RoutingResult:
    """
    路由结果
    
    包含路由决策的结果和相关信息。
    
    Attributes:
        primary_adapter: 主要适配器名称
        fallback_adapters: 备选适配器列表（按优先级排序）
        routing_reason: 路由原因说明
        confidence_score: 置信度分数 (0-1)
        metadata: 额外的元数据
    """
    primary_adapter: Optional[str] = None
    fallback_adapters: List[str] = field(default_factory=list)
    routing_reason: str = ""
    confidence_score: float = 1.0
    metadata: Dict[str, Any] = field(default_factory=dict)
    
    def is_valid(self) -> bool:
        """检查路由结果是否有效"""
        return self.primary_adapter is not None
    
    def get_all_adapters(self) -> List[str]:
        """获取所有适配器（主要 + 备选）"""
        adapters = []
        if self.primary_adapter:
            adapters.append(self.primary_adapter)
        adapters.extend(self.fallback_adapters)
        return adapters
    
    def add_fallback(self, adapter_name: str):
        """添加备选适配器"""
        if adapter_name not in self.fallback_adapters and adapter_name != self.primary_adapter:
            self.fallback_adapters.append(adapter_name)


class RoutingStrategy(ABC):
    """
    路由策略抽象基类
    
    定义路由策略的接口，所有具体策略必须实现此接口。
    
    策略模式的应用，支持：
    - 优先级路由
    - 能力匹配路由
    - 负载均衡路由
    - 性能优化路由
    - 轮询路由
    - 随机路由
    """
    
    def __init__(
        self,
        name: str,
        priority: RoutingPriority = RoutingPriority.MEDIUM,
        logger: Optional[logging.Logger] = None
    ):
        """
        初始化路由策略
        
        Args:
            name: 策略名称
            priority: 策略优先级
            logger: 日志记录器
        """
        self.name = name
        self.priority = priority
        self.logger = logger or logging.getLogger(__name__)
        self._enabled = True
    
    @abstractmethod
    async def select_adapter(
        self,
        context: RoutingContext,
        available_adapters: List[str]
    ) -> RoutingResult:
        """
        选择适配器
        
        Args:
            context: 路由上下文
            available_adapters: 可用的适配器列表
        
        Returns:
            RoutingResult: 路由结果
        """
        pass
    
    def is_enabled(self) -> bool:
        """检查策略是否启用"""
        return self._enabled
    
    def enable(self):
        """启用策略"""
        self._enabled = True
        self.logger.info(f"路由策略已启用: {self.name}")
    
    def disable(self):
        """禁用策略"""
        self._enabled = False
        self.logger.warning(f"路由策略已禁用: {self.name}")
    
    def get_priority_value(self) -> int:
        """获取优先级数值"""
        return self.priority.value
    
    async def can_handle(self, context: RoutingContext) -> bool:
        """
        检查策略是否能处理该上下文
        
        Args:
            context: 路由上下文
        
        Returns:
            bool: 是否能处理
        """
        return self._enabled
    
    def __repr__(self) -> str:
        return f"<{self.__class__.__name__}(name={self.name}, priority={self.priority.name}, enabled={self._enabled})>"


class CompositeRoutingStrategy(RoutingStrategy):
    """
    组合路由策略
    
    可以组合多个路由策略，按优先级依次尝试。
    """
    
    def __init__(
        self,
        name: str = "composite",
        strategies: Optional[List[RoutingStrategy]] = None,
        logger: Optional[logging.Logger] = None
    ):
        """
        初始化组合策略
        
        Args:
            name: 策略名称
            strategies: 子策略列表
            logger: 日志记录器
        """
        super().__init__(name=name, priority=RoutingPriority.HIGHEST, logger=logger)
        self.strategies: List[RoutingStrategy] = strategies or []
        self._sort_strategies()
    
    def add_strategy(self, strategy: RoutingStrategy):
        """添加子策略"""
        self.strategies.append(strategy)
        self._sort_strategies()
        self.logger.info(f"添加子策略: {strategy.name}")
    
    def remove_strategy(self, strategy_name: str) -> bool:
        """移除子策略"""
        for i, strategy in enumerate(self.strategies):
            if strategy.name == strategy_name:
                self.strategies.pop(i)
                self.logger.info(f"移除子策略: {strategy_name}")
                return True
        return False
    
    def _sort_strategies(self):
        """按优先级排序策略"""
        self.strategies.sort(key=lambda s: s.get_priority_value())
    
    async def select_adapter(
        self,
        context: RoutingContext,
        available_adapters: List[str]
    ) -> RoutingResult:
        """
        依次尝试各个子策略
        
        Args:
            context: 路由上下文
            available_adapters: 可用的适配器列表
        
        Returns:
            RoutingResult: 路由结果
        """
        for strategy in self.strategies:
            if not strategy.is_enabled():
                continue
            
            try:
                if await strategy.can_handle(context):
                    result = await strategy.select_adapter(context, available_adapters)
                    if result.is_valid():
                        result.metadata["selected_by_strategy"] = strategy.name
                        self.logger.info(
                            f"策略 {strategy.name} 选择了适配器: {result.primary_adapter}"
                        )
                        return result
            except Exception as e:
                self.logger.error(f"策略 {strategy.name} 执行失败: {e}")
                continue
        
        # 所有策略都失败，返回空结果
        self.logger.warning("所有路由策略都未能选择适配器")
        return RoutingResult(
            routing_reason="所有路由策略都失败",
            confidence_score=0.0
        )
    
    def get_strategy_count(self) -> int:
        """获取子策略数量"""
        return len(self.strategies)
    
    def get_enabled_strategy_count(self) -> int:
        """获取已启用的子策略数量"""
        return sum(1 for s in self.strategies if s.is_enabled())


# ==================== 工具函数 ====================

def create_routing_context(
    request_type: str,
    symbols: Optional[List[str]] = None,
    **kwargs
) -> RoutingContext:
    """
    便捷函数：创建路由上下文
    
    Args:
        request_type: 请求类型
        symbols: 证券代码列表
        **kwargs: 其他参数
    
    Returns:
        RoutingContext: 路由上下文
    """
    return RoutingContext(
        request_type=request_type,
        symbols=symbols or [],
        parameters=kwargs.get("parameters", {}),
        required_capabilities=set(kwargs.get("required_capabilities", [])),
        preferred_providers=kwargs.get("preferred_providers", []),
        excluded_providers=kwargs.get("excluded_providers", []),
        metadata=kwargs.get("metadata", {})
    )

